//=============================================================================
// Terrain_Heightmap.cpp by Frank Luna (C) 2004 All Rights Reserved.
//=============================================================================
#include "../../Base/StdAfx.h"
#include <fstream>
#include <vector>
#include "Terrain_Heightmap.h"

Terrain_Heightmap::Terrain_Heightmap()
{
}

Terrain_Heightmap::Terrain_Heightmap(int row, int col)
{
	Recreate(row, col);
}

Terrain_Heightmap::Terrain_Heightmap(int row, int col,  const tstring& filename, float heightScale, float heightOffset)
{
	LoadRAW16(row, col, filename, heightScale, heightOffset);
}

void Terrain_Heightmap::Recreate(int row, int col)
{
	m_HeightScale       = 1.0f;
	m_HeightOffset      = 0.0f;
	m_HeightMap.resize(row, col, 0.0f);
}

void Terrain_Heightmap::LoadRAW(int row, int col, const tstring& filename, float heightScale, float heightOffset)
{
	m_HeightMapFilename = filename;
	m_HeightScale       = heightScale;
	m_HeightOffset      = heightOffset;

	// A height for each vertex
	vector<unsigned char> in( row * col );

	// Open the file.
	std::ifstream inFile;
	inFile.open(filename.c_str(), ios_base::binary);
	if(!inFile) HR(E_FAIL);

	// Read the RAW bytes.
	inFile.read((char*)&in[0], (streamsize)in.size());

	// Done with file.
	inFile.close();

	// Copy the array data into a float table format and scale
	// the heights.
	m_HeightMap.resize(row, col, 0);
	for(int i = 0; i < row; ++i)//row
	{
		for(int j = 0; j < col; ++j)//colomn
		{
			int k = i * col + j;
			m_HeightMap(i, j) = (float)in[k] * heightScale + heightOffset;
		}
	}

	// Filter the table to smooth it out.  We do this because 256 height
	// steps is rather course.  And now that we copied the data into a
	// float-table, we have more precision.  So we can smooth things out
	// a bit by filtering the heights.
	Filter3x3();
}
void Terrain_Heightmap::LoadRAW16(int row, int col, const tstring& filename, float heightScale, float heightOffset)
{
	m_HeightMapFilename = filename;
	m_HeightScale       = heightScale;
	m_HeightOffset      = heightOffset;

	// A height for each vertex
	std::vector<unsigned short> in( row * col );

	// Open the file.
	std::ifstream inFile;
	inFile.open(filename.c_str(), ios_base::binary);
	if(!inFile) HR(E_FAIL);

	// Read the RAW bytes.
	inFile.read((char*)&in[0], (streamsize)(in.size()*sizeof(unsigned short)));

	// Done with file.
	inFile.close();

	// Copy the array data into a float table format and scale
	// the heights.
	m_HeightMap.resize(row, col, 0);
	for(int i = 0; i < row; ++i)//row
	{
		for(int j = 0; j < col; ++j)//colomn
		{
			int k = i * col + j;
			m_HeightMap(i, j) = (float)in[k] * heightScale + heightOffset;
		}
	}

	// Filter the table to smooth it out.  We do this because 256 height
	// steps is rather course.  And now that we copied the data into a
	// float-table, we have more precision.  So we can smooth things out
	// a bit by filtering the heights.
	//Filter3x3();
}
void Terrain_Heightmap::Filter3x3()
{
	Table<float> temp(m_HeightMap.numRows(), m_HeightMap.numCols());

	for(int i = 0; i < m_HeightMap.numRows(); ++i)
		for(int j = 0; j < m_HeightMap.numCols(); ++j)
			temp(i,j) = SampleHeight3x3(i,j);

	m_HeightMap = temp;
}

int Terrain_Heightmap::NumRows()const
{
	return m_HeightMap.numRows();
}

int Terrain_Heightmap::NumCols()const
{
	return m_HeightMap.numCols();
}

float Terrain_Heightmap::GetHeight(int i, int j)const
{
	return m_HeightMap(i, j);
}

bool Terrain_Heightmap::InBounds(int i, int j)
{
	return 
		i >= 0 && 
		i < (int)m_HeightMap.numRows() && 
		j >= 0 && 
		j < (int)m_HeightMap.numCols();
}

float Terrain_Heightmap::SampleHeight3x3(int i, int j)
{
	// Function computes the average height of the ij element.
	// It averages itself with its eight neighbor pixels.  Note
	// that if a pixel is missing neighbor, we just don't include it
	// in the average--that is, edge pixels don't have a neighbor pixel.
	//
	// ----------
	// | 1| 2| 3|
	// ----------
	// |4 |ij| 6|
	// ----------
	// | 7| 8| 9|
	// ----------

	float avg = 0.0f;
	float num = 0.0f;

	for(int row = i-1; row <= i+1; ++row)
	{
		for(int col = j-1; col <= j+1; ++col)
		{
			if( InBounds(row,col) )
			{
				avg += m_HeightMap(row,col);
				num += 1.0f;
			}
		}
	}

	return avg / num;
}